﻿/* *************************************************
 *  Copyright © FlexDev 2010. All rights reserved.
 *  Author: Rodrigo da Silva Brito
 *  Date: 07/16/2010
 *  Contact: mailto:rodrigosilvabrito@hotmail.com
 *  Site: http://www.flexdev.com.br
 * *************************************************
*/
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Flexdev.Security
{
    /// <summary>
    /// Class support for AES (Advanced Encryption Standard (AES))
    /// </summary>
    public class CryptService
    {
        #region Private

        // Encrypt a byte array into a byte array using a key and an IV 
        private static byte[] Encrypt(byte[] clearData, byte[] Key, byte[] IV)
        {
            // Create a MemoryStream that is going to accept the encrypted bytes 
            MemoryStream ms = new MemoryStream();

            AesManaged alg = new AesManaged();
            alg.Key = Key;
            alg.IV = IV;

            CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);

            cs.Write(clearData, 0, clearData.Length);
            cs.Close();

            byte[] encryptedData = ms.ToArray();

            return encryptedData;
        }

        // Decrypt a byte array into a byte array using a key and an IV 
        private static byte[] Decrypt(byte[] cipherData, byte[] Key, byte[] IV)
        {
            MemoryStream ms = new MemoryStream();

            AesManaged alg = new AesManaged();
            alg.Key = Key;
            alg.IV = IV;

            CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);

            cs.Write(cipherData, 0, cipherData.Length);
            cs.Close();

            byte[] decryptedData = ms.ToArray();

            return decryptedData;
        }

        #endregion

        #region Public

        /// <summary>
        /// Encrypts string using Rfc2898DeriveBytes (128,192,256 bits).
        /// </summary>
        /// <param name="data">String to be encrypted</param>
        /// <param name="password">Encryption password</param>
        /// <param name="bits">Encryption level (128,192,256 bits)</param>
        /// <returns>Encrypted string</returns>
        public static string Encrypt(string data, string password, AesCryptographyLevel bits)
        {
            if (string.IsNullOrEmpty(data)) data = string.Empty;

            byte[] clearBytes = Encoding.Unicode.GetBytes(data);

            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, new byte[] { 0x00, 0x01, 0x02, 0x1C, 0x1D, 0x1E, 0x03, 0x04, 0x05, 0x0F, 0x20, 0x21, 0xAD, 0xAF, 0xA4 });

            if (bits == AesCryptographyLevel.AES_128)
            {
                byte[] encryptedData = Encrypt(clearBytes, pdb.GetBytes(16), pdb.GetBytes(16));
                return Convert.ToBase64String(encryptedData);
            }
            else if (bits == AesCryptographyLevel.AES_192)
            {
                byte[] encryptedData = Encrypt(clearBytes, pdb.GetBytes(24), pdb.GetBytes(16));
                return Convert.ToBase64String(encryptedData);
            }
            else if (bits == AesCryptographyLevel.AES_256)
            {
                byte[] encryptedData = Encrypt(clearBytes, pdb.GetBytes(32), pdb.GetBytes(16));
                return Convert.ToBase64String(encryptedData);
            }
            else
            {
                return string.Concat(bits);
            }
        }

        /// <summary>
        /// Decrypt string using Rfc2898DeriveBytes (128,192,256 bits).
        /// </summary>
        /// <param name="data">String to be decrypted</param>
        /// <param name="password">Password decryption</param>
        /// <param name="bits">Level decryption (128,192,256 bits)</param>
        /// <returns>Decrypted string</returns>
        public static string Decrypt(string data, string password, AesCryptographyLevel bits)
        {
            if (string.IsNullOrEmpty(data)) data = string.Empty;
            byte[] cipherBytes = Convert.FromBase64String(data);

            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, new byte[] { 0x00, 0x01, 0x02, 0x1C, 0x1D, 0x1E, 0x03, 0x04, 0x05, 0x0F, 0x20, 0x21, 0xAD, 0xAF, 0xA4 });

            if (bits == AesCryptographyLevel.AES_128)
            {
                byte[] decryptedData = Decrypt(cipherBytes, pdb.GetBytes(16), pdb.GetBytes(16));
                return Encoding.Unicode.GetString(decryptedData, 0, decryptedData.Length);
            }
            else if (bits == AesCryptographyLevel.AES_192)
            {
                byte[] decryptedData = Decrypt(cipherBytes, pdb.GetBytes(24), pdb.GetBytes(16));
                return Encoding.Unicode.GetString(decryptedData, 0, decryptedData.Length);
            }
            else if (bits == AesCryptographyLevel.AES_256)
            {
                byte[] decryptedData = Decrypt(cipherBytes, pdb.GetBytes(32), pdb.GetBytes(16));
                return Encoding.Unicode.GetString(decryptedData, 0, decryptedData.Length);
            }
            else
            {
                return string.Concat(bits);
            }
        }

        #endregion
    }
}
